﻿
// 2020/8/26: 首个记录（根据env-sample-v2.h 2020/5/15版本修改）
// 2020/11/3: 新增气压、相对湿度、能见度、风向、风速、PM10、道路状况、车道交通状况、灾害情况等字段。道路交通状况更名
// 2020/11/12: 支持Spadas7
// 2020/11/26: 实现fromGeneralSample

#ifndef ENV_SAMPLE_V3_H
#define ENV_SAMPLE_V3_H

#include "spadas.h"

namespace env_sample_v3
{
	using namespace spadas;

#if SPADAS_VERSION_MAJOR < 7
	typedef OptionalDouble GeneralElement;
#endif

	enum WeatherType
	{
		WT_Unknown = 0, // 未知
		WT_Sunny = 1, // 晴
		WT_Cloudy = 2, // 阴
		WT_Rainy = 3, // 雨
		WT_Snowy = 4, // 雪
		WT_Foggy = 5, // 雾
		WT_Sand = 6, // 沙尘
	};

	enum RoadType
	{
		RT_Unknown = 0, // 未知
		RT_Highway = 1, // 高速路，无红绿灯，80~120KPH
		RT_CityExpress = 2, // 城市快速路，无红绿灯，60~80KPH
		RT_CityMain = 3, // 城市主干道，有红绿灯，推荐60KPH
		RT_GeneralRoad = 4, // 一般道路，有红绿灯，推荐40KPH
		RT_CountryRoad = 5, // 乡村道路，无红绿灯，~40KPH
	};

	enum SpotType
	{
		ST_Unknown = 0, // 未知
		ST_TollGate = 1, // 收费站
		ST_ServiceArea = 2, // 服务区
		ST_Tunnel = 3, // 隧道
		ST_Accident = 4, // 事故
		ST_Crossing = 5, // 路口
		ST_Roundabout = 7, // 环岛
		ST_HighwayEntrance = 8, // 高速入口
		ST_HighwayExit = 9, // 高速出口
		ST_Bridge = 10, // 桥
		ST_ExpressEntrance = 11, // 快速路入口
		ST_ExpressExit = 12, // 快速路出口
	};

	struct Spot
	{
		SpotType spotType; // 场所类型
		Double distance; // 到场所的距离 m
	};

	enum TrafficStatus
	{
		TF_Unknown = 0, // 未知
		TF_Open = 1, // 畅通
		TF_Slow = 2, // 缓慢
		TF_Crowded = 3, // 拥挤
		TF_Reverse = 4, // 逆向（仅限相邻车道交通状况）
	};

	enum WindDirection
	{
		WD_Unknown = 0, // 未知
		WD_East = 1, // 东风
		WD_South = 2, // 南风
		WD_West = 3, // 西风
		WD_North = 4, // 北风
		WD_SouthEast = 5, // 东南风
		WD_NorthEast = 6, // 东北风
		WD_SouthWest = 7, // 西南风
		WD_NorthWest = 8, // 西北风
	};

	enum RoadSurfaceStatus
	{
		RSS_Unknown = 0, // 未知
		RSS_Dry = 1, // 干燥
		RSS_Wet = 2, // 湿滑
		RSS_SnowCovered = 3, // 积雪
		RSS_Frozen = 4, // 结冰
	};

	enum DisasterStatus
	{
		DS_Unknown = 0, // 未知
		DS_No = 1, // 无灾害
		DS_Yes = 2, // 有灾害
	};

	struct EnvSample
	{
		GlobalTimestamp timeStamp; // 时间戳

		WeatherType weatherType; // 天气类型
		OptionalDouble temperature; // 温度 °C
		OptionalDouble humidity; // 相对湿度 %
		OptionalDouble pressure; // 气压 mb
		OptionalDouble pm25; // PM 2.5 ug/m3
		OptionalDouble pm10; // ug/m3
		OptionalDouble visibility; // 能见度 KM
		OptionalDouble illumination; // 光照强度 lux
		OptionalDouble windSpeed; // 风速 m/s
		WindDirection windDirection; // 风向
		DisasterStatus typhoonStatus; // 台风状况
		DisasterStatus tornadoStatus; // 龙卷风状况
		DisasterStatus lightningStatus; // 雷电状况
		DisasterStatus hailStatus; // 冰雹状况
		String roadName; // 道路名称
		OptionalInt speedLimit; // 道路限速 KPH
		RoadType roadType; // 道路类型
		RoadSurfaceStatus roadSurfaceStatus; // 路面状况
		TrafficStatus roadTraffic; // 道路交通状况
		TrafficStatus currentLaneTraffic; // 本车道交通状况
		TrafficStatus leftLaneTraffic; // 左车道交通状况
		TrafficStatus rightLaneTraffic; // 右车道交通状况
		Array<Spot> spots; // 场所列表

		EnvSample()
		{
			weatherType = WT_Unknown;
			windDirection = WD_Unknown;
			typhoonStatus = DS_Unknown;
			tornadoStatus = DS_Unknown;
			lightningStatus = DS_Unknown;
			hailStatus = DS_Unknown;
			roadType = RT_Unknown;
			roadSurfaceStatus = RSS_Unknown;
			roadTraffic = TF_Unknown;
			currentLaneTraffic = TF_Unknown;
			leftLaneTraffic = TF_Unknown;
			rightLaneTraffic = TF_Unknown;
		}

		static String getProtocolName()
		{
			return "env-sample-v3";
		}

		static String getTitle()
		{
			return "Road name size,Spot count,Weather,Temperature[°C],PM2.5[ug/m3],Speed limit[KPH],Road type,Illumination[lux],Wind speed[m/s],Road traffic status,Road name,Pressure[mb],Humidity[%],Visibility[KM],Wind direction,PM10[ug/m3],Road surface status,Current lane traffic status,Left lane traffic status,Right lane traffic status,Typhoon status,Tornado status,Lightning status,Hail status,...";
		}

		static Bool supportInterpolation()
		{
			return FALSE;
		}

		GeneralSample toGeneralSample()
		{
#if SPADAS_VERSION_MAJOR < 7
			Binary roadNameBin = roadName.toBinary();
#else
			Binary roadNameBin;
#endif

			GeneralSample sample;
			sample.timeStamp = timeStamp;
			sample.values = Array<GeneralElement>(64 + spots.size() * 2 + roadNameBin.size());

			sample.values[1] = spots.size();

			sample.values[2] = (UInt)weatherType;
			sample.values[3] = temperature;
			sample.values[4] = pm25;
			sample.values[5] = speedLimit.valid ? speedLimit.value : OptionalDouble();
			sample.values[6] = (UInt)roadType;
			sample.values[7] = illumination;
			sample.values[8] = windSpeed;
			sample.values[9] = (UInt)roadTraffic;

			sample.values[11] = pressure;
			sample.values[12] = humidity;
			sample.values[13] = visibility;
			sample.values[14] = (UInt)windDirection;
			sample.values[15] = pm10;
			sample.values[16] = (UInt)roadSurfaceStatus;
			sample.values[17] = (UInt)currentLaneTraffic;
			sample.values[18] = (UInt)leftLaneTraffic;
			sample.values[19] = (UInt)rightLaneTraffic;
			sample.values[20] = (UInt)typhoonStatus;
			sample.values[21] = (UInt)tornadoStatus;
			sample.values[22] = (UInt)lightningStatus;
			sample.values[23] = (UInt)hailStatus;

			for (UInt i = 0; i < spots.size(); i++)
			{
				sample.values[64 + 2 * i] = (UInt)spots[i].spotType;
				sample.values[64 + 2 * i + 1] = spots[i].distance;
			}

#if SPADAS_VERSION_MAJOR < 7
			sample.values[0] = roadNameBin.size();
			for (UInt i = 0; i < roadNameBin.size(); i++)
			{
				sample.values[64 + spots.size() * 2 + i] = roadNameBin[i];
			}
#else
			sample.values[10] = roadName;
#endif

			sample.significantCount = sample.values.size();
			return sample;
		}

		Bool fromGeneralSample(GeneralSample sample)
		{
			return fromGeneralSample(getProtocolName(), sample);
		}

		Bool fromGeneralSample(String protocol, GeneralSample sample)
		{
			if (protocol == "env-sample-v3")
			{
				if (sample.values.size() < 2) return FALSE;

				UInt roadNameBinSize = (UInt)sample.values[0].value;
				UInt spotsCount = (UInt)sample.values[1].value;
				if (sample.values.size() != 64 + spotsCount * 2 + roadNameBinSize) return FALSE;

				timeStamp = sample.timeStamp;

				weatherType = sample.values[2].valid ? (WeatherType)(Int)sample.values[2].value : WT_Unknown;
				temperature = sample.values[3];
				pm25 = sample.values[4];
				speedLimit = sample.values[5].valid ? (Int)sample.values[5].value : OptionalInt();
				roadType = sample.values[6].valid ? (RoadType)(Int)sample.values[6].value : RT_Unknown;
				illumination = sample.values[7];
				windSpeed = sample.values[8];
				roadTraffic = sample.values[9].valid ? (TrafficStatus)(Int)sample.values[9].value : TF_Unknown;

				pressure = sample.values[11];
				humidity = sample.values[12];
				visibility = sample.values[13];
				windDirection = sample.values[14].valid ? (WindDirection)(Int)sample.values[14].value : WD_Unknown;
				pm10 = sample.values[15];
				roadSurfaceStatus = sample.values[16].valid ? (RoadSurfaceStatus)(Int)sample.values[16].value : RSS_Unknown;
				currentLaneTraffic = sample.values[17].valid ? (TrafficStatus)(Int)sample.values[17].value : TF_Unknown;
				leftLaneTraffic = sample.values[18].valid ? (TrafficStatus)(Int)sample.values[18].value : TF_Unknown;
				rightLaneTraffic = sample.values[19].valid ? (TrafficStatus)(Int)sample.values[19].value : TF_Unknown;
				typhoonStatus = sample.values[20].valid ? (DisasterStatus)(Int)sample.values[20].value : DS_Unknown;
				tornadoStatus = sample.values[21].valid ? (DisasterStatus)(Int)sample.values[21].value : DS_Unknown;
				lightningStatus = sample.values[22].valid ? (DisasterStatus)(Int)sample.values[22].value : DS_Unknown;
				hailStatus = sample.values[23].valid ? (DisasterStatus)(Int)sample.values[23].value : DS_Unknown;

				spots = Array<Spot>(spotsCount);
				for (UInt i = 0; i < spots.size(); i++)
				{
					spots[i].spotType = (SpotType)(Int)sample.values[64 + 2 * i].value;
					spots[i].distance = sample.values[64 + 2 * i + 1].value;
				}

				if (roadNameBinSize > 0)
				{
					Binary roadNameBin(roadNameBinSize);
					for (UInt i = 0; i < roadNameBinSize; i++)
					{
						roadNameBin[i] = (Byte)sample.values[64 + spots.size() * 2 + i].value;
					}
					roadName = roadNameBin;
				}
				else roadName = String();

#if SPADAS_VERSION_MAJOR >= 7
				if (roadName.isEmpty() && sample.values[10].valid && sample.values[10].isText) roadName = sample.values[10].text;
#endif

				return TRUE;
			}
			else if (protocol == "env-sample-v2")
			{
				if (sample.values.size() < 2) return FALSE;

				UInt roadNameBinSize = (UInt)sample.values[0].value;
				UInt spotsCount = (UInt)sample.values[1].value;
				if (sample.values.size() != 10 + spotsCount * 2 + roadNameBinSize) return FALSE;

				timeStamp = sample.timeStamp;

				weatherType = sample.values[2].valid ? (WeatherType)(Int)sample.values[2].value : WT_Unknown;
				temperature = sample.values[3];
				pm25 = sample.values[4];
				speedLimit = sample.values[5].valid ? (Int)sample.values[5].value : OptionalInt();
				roadType = sample.values[6].valid ? (RoadType)(Int)sample.values[6].value : RT_Unknown;
				illumination = sample.values[7];
				windSpeed = sample.values[8];
				roadTraffic = sample.values[9].valid ? (TrafficStatus)(Int)sample.values[9].value : TF_Unknown;

				spots = Array<Spot>(spotsCount);
				for (UInt i = 0; i < spots.size(); i++)
				{
					spots[i].spotType = (SpotType)(Int)sample.values[10 + 2 * i].value;
					spots[i].distance = sample.values[10 + 2 * i + 1].value;
				}

				if (roadNameBinSize > 0)
				{
					Binary roadNameBin(roadNameBinSize);
					for (UInt i = 0; i < roadNameBinSize; i++)
					{
						roadNameBin[i] = (Byte)sample.values[10 + spots.size() * 2 + i].value;
					}
					roadName = roadNameBin;
				}
				else roadName = String();

				return TRUE;
			}

			return FALSE;
		}

		static EnvSample interpolate(EnvSample& s1, Double w1, EnvSample& s2, Double w2, GlobalTimestamp time)
		{
			return EnvSample();
		}
	};
}

#endif
